{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\".\\\\diyLogo.png\" alt=\"some_text\">\n",
    "<h1> 第五讲 输入与输出基础</h1>\n",
    "<a id=backup></a>\n",
    "<H2>目录</H2>  \n",
    "\n",
    "[5.1 输出格式](#Section1)     \n",
    "[5.2 读写文件](#Section2) "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<a id = Section1> </a>\n",
    "## 5.1 输出格式\n",
    "输出方式\n",
    "+ 表达式语句\n",
    "+ print() 函数\n",
    "+ 文件对象的 write() 方法\n",
    "+ 标准输出文件称为 sys.stdout\n",
    "\n",
    "对输出格式的控制不只是打印空格分隔的值，还需要更多方式。格式化输出包括以下几种方法:\n",
    "\n",
    "+ 使用 格式化字符串字面值 ，要在字符串开头的引号/ 三引号前添加 f 或 F 。在这种字符串中，可以在 { 和 } 字符之间输入引用的变量，或字面值的 Python 表达式。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "year = 2018\n",
    "event = 'Referendum'\n",
    "f'Results of the {year} {event}'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "+ 字符串的 str.format() 方法需要更多手动操作。该方法也用 { 和 } 标记替换变量的位置，虽然这种方法支持详细的格式化指令，但需要提供格式化信息。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "yes_votes = 42_572_654\n",
    "no_votes = 43_132_495\n",
    "percentage = yes_votes / (yes_votes + no_votes)\n",
    "'{:-9} YES votes  {:2.2%}'.format(yes_votes, percentage)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "+ 最后，还可以用字符串切片和合并操作完成字符串处理操作，创建任何排版布局。字符串类型还支持将字符串按给定列宽进行填充，这些方法也很有用。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如果不需要花哨的输出，只想快速显示变量进行调试，可以用 repr() 或 str() 函数把值转化为字符串。\n",
    "str() 函数返回供人阅读的值，repr() 则生成适于解释器读取的值（如果没有等效的语法，则强制执行 SyntaxError）。对于没有支持供人阅读展示结果的对象， str() 返回与 repr() 相同的值。一般情况下，数字、列表或字典等结构的值，使用这两个函数输出的表现形式是一样的。字符串有两种不同的表现形式。\n",
    "\n",
    "示例如下："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "s = 'Hello, world.'\n",
    "str(s)\n",
    "repr(s)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "str(1/7)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x = 10 * 3.25\n",
    "y = 200 * 200\n",
    "s = 'The value of x is ' + repr(x) + ', and y is ' + repr(y) + '...'\n",
    "print(s)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# The repr() of a string adds string quotes and backslashes:\n",
    "hello = 'hello, world\\n'\n",
    "hellos = repr(hello)\n",
    "print(hellos)\n",
    "'hello, world\\n'\n",
    "# The argument to repr() may be any Python object:\n",
    "repr((x, y, ('spam', 'eggs')))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "string 模块包含 Template 类，提供了将值替换为字符串的另一种方法。该类使用 $x 占位符，并用字典的值进行替换，但对格式控制的支持比较有限。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 5.1.1. 格式化字符串字面值\n",
    "格式化字符串字面值 （简称为 f-字符串）在字符串前加前缀 f 或 F，通过 {expression} 表达式，把 Python 表达式的值添加到字符串内。\n",
    "\n",
    "格式说明符是可选的，写在表达式后面，可以更好地控制格式化值的方式。下例将 pi 舍入到小数点后三位："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import math\n",
    "print(math.pi)\n",
    "print(f'The value of pi is approximately {math.pi:.3f}.')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在 ':' 后传递整数，为该字段设置最小字符宽度，常用于列对齐："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "table = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 7678}\n",
    "for name, phone in table.items():\n",
    "    print(f'{name:10} ==> {phone:10d}')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "还有一些修饰符可以在格式化前转换值。 '!a' 应用 ascii() ，'!s' 应用 str()，'!r' 应用 repr()："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "animals = 'eels'\n",
    "print(f'My hovercraft is full of {animals}.')\n",
    "\n",
    "print(f'My hovercraft is full of {animals!r}.')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 5.1.2. 字符串 format() 方法\n",
    "str.format() 方法的基本用法如下所示："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print('We are the {} who say \"{}!\"'.format('knights', 'Ni'))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "花括号及之内的字符（称为格式字段）被替换为传递给 str.format() 方法的对象。花括号中的数字表示传递给 str.format() 方法的对象所在的位置。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print('{0} and {1}'.format('spam', 'eggs'))\n",
    "\n",
    "print('{1} and {0}'.format('spam', 'eggs'))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "str.format() 方法中使用关键字参数名引用值。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print('This {food} is {adjective}.'.format(\n",
    "      food='spam', adjective='absolutely horrible'))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "位置参数和关键字参数可以任意组合："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print('The story of {0}, {1}, and {other}.'.format('Bill', 'Manfred',other='Georg'))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如果不想分拆较长的格式字符串，最好按名称引用变量进行格式化，不要按位置。这项操作可以通过传递字典，并用方括号 '[]' 访问键来完成。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "table = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 8637678}\n",
    "print('Jack: {0[Jack]:d}; Sjoerd: {0[Sjoerd]:d}; '\n",
    "      'Dcab: {0[Dcab]:d}'.format(table))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "也可以用 '**' 符号，把字典 table 当作传递的关键字参数。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "table = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 8637678}\n",
    "print('Jack: {Jack:d}; Sjoerd: {Sjoerd:d}; Dcab: {Dcab:d}'.format(**table))\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "与内置函数 vars() 结合使用时，这种方式非常实用，可以返回包含所有局部变量的字典。\n",
    "\n",
    "例如，下面的代码生成一组整齐的列，包含给定整数及其平方与立方："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "for x in range(1, 11):\n",
    "    print('{0:2d} {1:3d} {2:4d}'.format(x, x*x, x*x*x))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 5.1.3. 手动格式化字符串\n",
    "下面是使用手动格式化方式实现的同一个平方和立方的表："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "for x in range(1, 11):\n",
    "    print(repr(x).rjust(2), repr(x*x).rjust(3), end=' ')\n",
    "    # Note use of 'end' on previous line\n",
    "    print(repr(x*x*x).rjust(4))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "（注意，每列之间的空格是通过使用 print() 添加的：它总在其参数间添加空格。）\n",
    "\n",
    "字符串对象的 str.rjust() 方法通过在左侧填充空格，对给定宽度字段中的字符串进行右对齐。同类方法还有 str.ljust() 和 str.center() 。这些方法不写入任何内容，只返回一个新字符串，如果输入的字符串太长，它们不会截断字符串，而是原样返回；虽然这种方式会弄乱列布局，但也比另一种方法好，后者在显示值时可能不准确（如果真的想截断字符串，可以使用 x.ljust(n)[:n] 这样的切片操作 。）\n",
    "\n",
    "另一种方法是 str.zfill() ，该方法在数字字符串左边填充零，且能识别正负号："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "'12'.zfill(5)\n",
    "\n",
    "'-3.14'.zfill(7)\n",
    "\n",
    "'3.14159265359'.zfill(5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 5.1.4. 旧式字符串格式化方法\n",
    "% 运算符（求余符）也可用于字符串格式化。给定 'string' % values，则 string 中的 % 实例会以零个或多个 values 元素替换。此操作被称为字符串插值。例如"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import math\n",
    "print('The value of pi is approximately %10.7f.' % math.pi)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 5.2. 读写文件\n",
    "open() 返回 file object，最常用的参数有两个: open(filename, mode)。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "f = open('workfile', 'r')\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "f.closed\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "第一个实参是文件名字符串。第二个实参是包含描述文件使用方式字符的字符串。mode 的值包括 'r' ，表示文件只能读取；'w' 表示只能写入（现有同名文件会被覆盖）；'a' 表示打开文件并追加内容，任何写入的数据会自动添加到文件末尾。'r+' 表示打开文件进行读写。mode 实参是可选的，省略时的默认值为 'r'。\n",
    "\n",
    "通常，文件以 text mode 打开，即，从文件中读取或写入字符串时，都以指定编码方式进行编码。如未指定编码格式，默认值与平台相关 (参见 open())。在 mode 中追加的 'b' 则以 binary mode 打开文件：此时，数据以字节对象的形式进行读写。该模式用于所有不包含文本的文件。\n",
    "\n",
    "在文本模式下读取文件时，默认把平台特定的行结束符（Unix 上为 \\n, Windows 上为 \\r\\n）转换为 \\n。在文本模式下写入数据时，默认把 \\n 转换回平台特定结束符。这种操作方式在后台修改文件数据对文本文件来说没有问题，但会破坏 JPEG 或 EXE 等二进制文件中的数据。注意，在读写此类文件时，一定要使用二进制模式。\n",
    "\n",
    "在处理文件对象时，最好使用 with 关键字。优点是，子句体结束后，文件会正确关闭，即便触发异常也可以。而且，使用 with 相比等效的 try-finally 代码块要简短得多："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "with open('workfile') as f:\n",
    "    read_data = f.read()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# We can check that the file has been automatically closed.\n",
    "f.closed\n",
    "print(read_data)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如果没有使用 with 关键字，则应调用 f.close() 关闭文件，即可释放文件占用的系统资源。\n",
    "\n",
    "警告 调用 f.write() 时，未使用 with 关键字，或未调用 f.close()，即使程序正常退出，也**可能** 导致 f.write() 的参数没有完全写入磁盘。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "通过 with 语句，或调用 f.close() 关闭文件对象后，再次使用该文件对象将会失败。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "f.close()\n",
    "f.read()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 5.2.1. 文件对象的方法¶\n",
    "本节下文中的例子假定已创建 f 文件对象。\n",
    "\n",
    "f.read(size) 可用于读取文件内容，它会读取一些数据，并返回字符串（文本模式），或字节串对象（在二进制模式下）。 size 是可选的数值参数。省略 size 或 size 为负数时，读取并返回整个文件的内容；文件大小是内存的两倍时，会出现问题。size 取其他值时，读取并返回最多 size 个字符（文本模式）或 size 个字节（二进制模式）。如已到达文件末尾，f.read() 返回空字符串（''）。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "f = open('workfile', 'r')\n",
    "f.read()\n",
    "\n",
    "#f.read()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "f.readline() 从文件中读取单行数据；字符串末尾保留换行符（\\n），只有在文件不以换行符结尾时，文件的最后一行才会省略换行符。这种方式让返回值清晰明确；只要 f.readline() 返回空字符串，就表示已经到达了文件末尾，空行使用 '\\n' 表示，该字符串只包含一个换行符。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "f.close()\n",
    "f = open('workfile', 'r')\n",
    "f.readline()\n",
    "\n",
    "f.readline()\n",
    "\n",
    "#f.readline()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "从文件中读取多行时，可以用循环遍历整个文件对象。这种操作能高效利用内存，快速，且代码简单："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "f.close()\n",
    "f = open('workfile', 'r')\n",
    "for line in f:\n",
    "    print(line, end='')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "f.close()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如需以列表形式读取文件中的所有行，可以用 list(f) 或 f.readlines()。\n",
    "\n",
    "f.write(string) 把 string 的内容写入文件，并返回写入的字符数。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "f = open('workfile', 'a')\n",
    "f.write('This is a test\\n')\n",
    "f.close()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "写入其他类型的对象前，要先把它们转化为字符串（文本模式）或字节对象（二进制模式）："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "value = ('the answer', 42)\n",
    "s = str(value)  # convert the tuple to string\n",
    "f.write(s)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "f.tell() 返回整数，给出文件对象在文件中的当前位置，表示为二进制模式下时从文件开始的字节数，以及文本模式下的意义不明的数字。\n",
    "\n",
    "f.seek(offset, whence) 可以改变文件对象的位置。通过向参考点添加 offset 计算位置；参考点由 whence 参数指定。 whence 值为 0 时，表示从文件开头计算，1 表示使用当前文件位置，2 表示使用文件末尾作为参考点。省略 whence 时，其默认值为 0，即使用文件开头作为参考点。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "f = open('workfile', 'rb+')\n",
    "f.write(b'0123456789abcdef')\n",
    "\n",
    "f.seek(5)      # Go to the 6th byte in the file\n",
    "\n",
    "f.read(1)\n",
    "\n",
    "f.seek(-3, 2)  # Go to the 3rd byte before the end\n",
    "\n",
    "f.read(1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在文本文件（模式字符串未使用 b 时打开的文件）中，只允许相对于文件开头搜索（使用 seek(0, 2) 搜索到文件末尾是个例外），唯一有效的 offset 值是能从 f.tell() 中返回的，或 0。其他 offset 值都会产生未定义的行为。\n",
    "\n",
    "文件对象还支持 isatty() 和 truncate() 等方法，但不常用；文件对象的完整指南详见库参考。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 5.2.2. 使用 json 保存结构化数据\n",
    "从文件写入或读取字符串很简单，数字则稍显麻烦，因为 read() 方法只返回字符串，这些字符串必须传递给 int() 这样的函数，接受 '123' 这样的字符串，并返回数字值 123。保存嵌套列表、字典等复杂数据类型时，手动解析和序列化的操作非常复杂。\n",
    "\n",
    "Python 支持 JSON (JavaScript Object Notation) 这种流行数据交换格式，用户无需没完没了地编写、调试代码，才能把复杂的数据类型保存到文件。json 标准模块采用 Python 数据层次结构，并将之转换为字符串表示形式；这个过程称为 serializing （序列化）。从字符串表示中重建数据称为 deserializing （解序化）。在序列化和解序化之间，表示对象的字符串可能已经存储在文件或数据中，或通过网络连接发送到远方 的机器。\n",
    "\n",
    "注解: JSON 格式通常用于现代应用程序的数据交换。程序员早已对它耳熟能详，可谓是交互操作的不二之选。\n",
    "\n",
    "只需一行简单的代码即可查看某个对象的 JSON 字符串表现形式："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import json\n",
    "x = [1, 'simple', 'list']\n",
    "json.dumps(x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "dumps() 函数还有一个变体， dump() ，它只将对象序列化为 text file 。因此，如果 f 是 text file 对象，可以这样做："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "json.dump(x, f)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "要再次解码对象，如果 f 是已打开、供读取的 text file 对象："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x = json.load(f)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这种简单的序列化技术可以处理列表和字典，但在 JSON 中序列化任意类的实例，则需要付出额外努力。json 模块的参考包含对此的解释。\n",
    "\n",
    "参见 pickle - 封存模块\n",
    "\n",
    "与 JSON 不同，pickle 是一种允许对复杂 Python 对象进行序列化的协议。因此，它为 Python 所特有，不能用于与其他语言编写的应用程序通信。默认情况下它也是不安全的：如果解序化的数据是由手段高明的攻击者精心设计的，这种不受信任来源的 pickle 数据可以执行任意代码。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Python打包分发工具setuptools\n",
    "曾经 Python 的分发工具是 distutils，但它无法定义包之间的依赖关系。setuptools 则是它的增强版，能帮助我们更好的创建和分发 Python 包，尤其是具有复杂依赖关系的包。其通过添加一个基本的依赖系统以及许多相关功能，弥补了该缺陷。他还提供了自动包查询程序，用来自动获取包之间的依赖关系，并完成这些包的安装，大大降低了安装各种包的难度，使之更加方便。\n",
    "\n",
    "现在 Python 安装会自带 setuptools\n",
    "setuptools 简单易用，只需写一个简短的 setup.py 安装文件，就可以将你的 Python 应用打包。\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3.10.4 ('python310')",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.4 | packaged by conda-forge | (main, Mar 30 2022, 08:38:02) [MSC v.1916 64 bit (AMD64)]"
  },
  "orig_nbformat": 4,
  "vscode": {
   "interpreter": {
    "hash": "a888cd810c90be07c3c921372a6d7ff433f5a38d5779dfb2a871d99db75673fb"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
